﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
//using System.Dynamic;

namespace wsserver
{		
	class Server
    {
        private TcpListener listener;	// Listens for connections from TCP network clients
        private List<Thread> activeThreads;
		public KoAbra game;
		
		struct C_game_status
		{
			public const int NOT_STARTED = 0;
			public const int STARTED = 1;
			public const int PAUSED = 2;
			public const int STOPED = 3;
		}
		
		struct C_errors
		{
			public const int ALL_RIGHT = 0;
			public const int NOT_ALL_PLAYERS_ARE_READY = 1;
			public const int ONE_TEAM_IS_EMPTY = 2;
		}
		
		struct C_Resources
		{
			public const int NONE = 0;		// ничего
			public const int GOLD = 1;		// золото
			public const int SILVER = 2;	// серебро
			public const int CUPRUM = 3;	// медь
			public const int STANNUM = 4;	// олово
			public const int WOOD = 5;		// лес
			public const int FOOD = 6;		// еда
		}

        // список активных игр
		public List<KoAbra> KoAbras
		{
			get
			{
				return _koabras;
			}
		}		
		private List<KoAbra> _koabras;
		
		//клиенты, что подключены к серверу
        public List<User> Users
        {
            get
            {
                return _users;
            }
        }

        private List<User> _users;


        public IPAddress IP
        {
            get
            {
                return _ip;
            }
        }

        private IPAddress _ip;

        public int Port
        {
            get
            {
                return _port;
            }
        }

        private int _port;

        public bool Log
        {
            get;
            set;
        }

        //конструктор сервера по умолчанию
        public Server()
        {
            _users = new List<User>();
			_koabras = new List<KoAbra>();
            _port = 8181;
            _ip = IPAddress.Parse("0.0.0.0");
            Log = true;
        }

        //констуктор с параметрами
        public Server(IPAddress ip, int port, bool log)
        {
            _users = new List<User>();
			_koabras = new List<KoAbra>();
            _port = port;
            _ip = ip;
            Log = log;
        }
		
        static byte[] BuildKey(string data)
        {
            int spaceCount = 0;
            string partialKey = "";
            char[] keyChars = data.ToCharArray();
            foreach (char c in keyChars)
            {
                if (char.IsDigit(c))
                    partialKey += c;
                if (char.IsWhiteSpace(c))
                    spaceCount++;
            }
            byte[] answ = BitConverter.GetBytes((int)(Int64.Parse(partialKey) / spaceCount));
            if (BitConverter.IsLittleEndian)
                Array.Reverse(answ);
            return answ;
        }


        private void Handshake(object obj)
        {
            var client = (TcpClient)obj;
            try
            {
                var stream = client.GetStream();
                byte[] data = new byte[1000];
                int read = 0;
                StringBuilder clientHandshakeBuilder = new StringBuilder();
                read = stream.Read(data, 0, 1000);
                //last 8 bytes - handshake secret
                clientHandshakeBuilder.Append(Encoding.ASCII.GetString(data, 0, read - 8));

                byte[] key1 = new byte[4], key2 = new byte[4];

                string[] handshakeLines = clientHandshakeBuilder.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

                string origin = "", location = "";

                foreach (string line in handshakeLines)
                {
                    LogIn(line);
                    if (line.StartsWith("Sec-WebSocket-Key1:"))
                        key1 = BuildKey(line.Substring(line.IndexOf(":") + 2));
                    if (line.StartsWith("Sec-WebSocket-Key2:"))
                        key2 = BuildKey(line.Substring(line.IndexOf(":") + 2));
                    if (line.StartsWith("Origin:"))
                        origin = line.Substring(line.IndexOf(":") + 2);
                    if (line.StartsWith("Host:"))
                        location = "ws://" + line.Substring(line.IndexOf(":") + 2) + "/test";
                }

                byte[] lastKey = new byte[8];
                Array.Copy(data, read - 8, lastKey, 0, 8);

                //All screts together
                byte[] all = new byte[16];
                Array.Copy(key1, all, 4);
                Array.Copy(key2, 0, all, 4, 4);
                Array.Copy(lastKey, 0, all, 8, 8);

                //Finish
                byte[] handshakeAnswer = MD5.Create().ComputeHash(all);
                string handshake = "HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine;
                handshake += "Upgrade: WebSocket" + Environment.NewLine;
                handshake += "Connection: Upgrade" + Environment.NewLine;
                handshake += "Sec-WebSocket-Origin: " + origin + Environment.NewLine;
                handshake += "Sec-WebSocket-Location: " + location + Environment.NewLine;
                handshake += Environment.NewLine;

                byte[] handshakeBytes = new byte[Encoding.ASCII.GetByteCount(handshake) + 16];
                Array.Copy(Encoding.UTF8.GetBytes(handshake), handshakeBytes, handshake.Length);
                Array.Copy(handshakeAnswer, 0, handshakeBytes, handshake.Length, 16);
                LogOut("Handshake");
                stream.Write(handshakeBytes, 0, handshakeBytes.Length);

                User u = new User(client, Recieve, Remove);
                _users.Add(u);
            }
            catch
            {
                client.Close();
            }
            finally
            {
                activeThreads.Remove(Thread.CurrentThread);
            }
        }

        //запуск сервера
        public void Start()
        {
            listener = new TcpListener(_ip, _port);
            activeThreads = new List<Thread>();
            listener.Start();
            Thread processConnectionsThread = new Thread(ProcessConnections);
            processConnectionsThread.Start();
            activeThreads.Add(processConnectionsThread);
        }

        //остановка сервера
        public void Stop()
        {
            foreach (Thread t in activeThreads)
                t.Abort();
            for (int i = 0; i < _users.Count; i++)
                _users[i].Stop();
            listener.Stop();
        }

        private void ProcessConnections()
        {
            while (true)
            {
                TcpClient client = listener.AcceptTcpClient();
                Thread handshakeThread = new Thread(Handshake);
                handshakeThread.Start(client);
                activeThreads.Add(handshakeThread);
            }
        }
		
		//user отключился
        //<***************************************
		//*** Server -> Client ***
		//[3.S]	[+][server][client]
		//{type: ChatUserDisconnect, login: nickname}
		//***************************************>
		private void Remove(User user)
        {
            if (_users.Contains(user))
            {
				PackageChatAuth PCAobj = new PackageChatAuth();
				PCAobj.type = "ChatUserDisconnect";
				PCAobj.login = user.Nick;
				string PCAjson = JsonConvert.SerializeObject(PCAobj);
				SendAll(PCAjson);
                _users.Remove(user);
                LogIn(String.Format("User {0} disconnected", user.Nick));
                //SendAll("\"" + user.Nick + "\" Отключился");
            }
        }
		
		// игра отключена
        //<***************************************
		//*** Server -> Client ***
		//[17.S]	[+][~server][~client]
		//{type: Action, action: Remove_game, name: gamename}
		//***************************************>
		private void RemoveGame(KoAbra game)
        {
            if (_koabras.Contains(game))
            {
				PackageRemoveGame PRGobj = new PackageRemoveGame();
				PRGobj.type = "Action";
				PRGobj.action = "Remove_game";
				PRGobj.name = game.name;
				string PRGjson = JsonConvert.SerializeObject(PRGobj);
				SendAll(PRGjson); //исправить
                _koabras.Remove(game);
                LogIn(String.Format("Game {0} disconnected", game.name));
            }
        }

        //отправка сообщения всем клиентам
        private void SendAll(string message)
        {
            for(int i = 0; i < _users.Count; i++)
               if(_users[i].Alive)
                   _users[i].SendMessage(message);
           // Users.RemoveAll(user => user.Alive == false);
        }
		
		//отправка сообщения клинету, приславшему сообщение
        private void SendThis(User user, string message)
        {
			user.SendMessage(message);
        }
		
		//отправка сообщения пользователям из определенной команды
		private void SendTeam(string message, string team_name)
		{
			if(team_name == "A")
			{
				for(int i = 0; i < game._a_game_users.Count; i++)
				{
					game._a_game_users[i].SendMessage(message);
				}
			}
			if(team_name == "B")
			{
				for(int i = 0; i < game._b_game_users.Count; i++)
				{
					game._b_game_users[i].SendMessage(message);
				}
			}
		}
		
		// отправить сообщение команде, за исключением текущего клиента
		private void SendTeamWithExceptionThis(string message, string team_name, string this_nick)
		{
			if(team_name == "A")
			{
				for(int i = 0; i < game._a_game_users.Count; i++)
				{
					if(game._a_game_users[i].Nick != this_nick)
					{
						game._a_game_users[i].SendMessage(message);
					}
				}
			}
			if(team_name == "B")
			{
				for(int i = 0; i < game._b_game_users.Count; i++)
				{
					if(game._b_game_users[i].Nick != this_nick)
					{
						game._b_game_users[i].SendMessage(message);
					}
				}
			}
		}
		
		// отправить сообщение всем, за исключением текущего клиента
		private void SendAllWithExceptionThis(string message, string this_nick)
		{
			for(int i = 0; i < _users.Count; i++)
			{
				if(_users[i].Nick != this_nick)
				{
					_users[i].SendMessage(message);
				}
			}
		}
		
        //процесс приема сообщений
        private void Recieve(User user, string message)
        {
			// если user аутентифинирован
			
            if (user.Authentificated)
            {
                LogIn(String.Format("Message Recieved (from {0}): {1}", user.Nick, message));
                
				//<***************************************
				//=== Обработка сообщений из чата ===
				//*** Client -> Server ***
				//[1.C]	[+][server][client]
				//{type: ChatMessage, message: содержимое сообщения}
				//*** Server -> Client ***
				//[1.S]	[+][server][client]
				//{type: ChatMessage, login: nickname, message: содержимое сообщения}
				//***************************************>
				JObject jo = JObject.Parse(message.ToString());
				if((string)jo["type"] == "ChatMessage")
				{
					PackageChatMessage PCMobj = new PackageChatMessage();
					PCMobj.login = user.Nick;
					PCMobj.message = (string)jo["message"];
					LogIn(message);
					
					string PCMjson = JsonConvert.SerializeObject(PCMobj);
					SendAll(PCMjson);
				}
				//***************************************>
				//SendAll(String.Format("\"{0}\": {1}", user.Nick, message.Replace("\n", "")));
				
				//<***************************************
				//=== Обработка сообщений типа Action ===							
				if((string)jo["type"] == "Action")
				{
					//<***************************************
					//===Пользователь желает создать игру===
					//*** Client -> Server ***
					//[3.C]	[+][server]
					//{"type": "Action", "action": "Create_game", "name": "gamename"}
					//*** Server -> Client ***
					//[5.S]	[+][server]
					//{"type": "Action", "action": "Create_game", "status": "OK", "reason": "0"}
					//[6.S]	[+][server]
					//{"type": "Action", "action": "Create_game", "status": "ERR", "reason": "номер_причины"}
					if((string)jo["action"] == "Create_game")
					{
						bool all_right = true;
						for(int i = 0; i < _koabras.Count; i++)
						{
							if(_koabras[i].name == (string)jo["name"])
							{
								all_right = false;
							}
						}
						PackageResult PRobj = new PackageResult();
						PRobj.action = "Create_game";
						if(all_right)
						{
							game = new KoAbra(user, (string)jo["name"]);		// создаем новую игру	
							_koabras.Add(game);					// добавляем созданную игру в список игр
							PRobj.status = "OK";
							PRobj.reason = "0";
						}					
						else
						{
							PRobj.status = "ERR";
							PRobj.reason = "1";		// игра с таким именем существует на сервере
						}
						string PRjson = JsonConvert.SerializeObject(PRobj);
						
						SendThis(user, PRjson);
					}
					//***************************************>
					
					//<***************************************
					//===Пользователь желает получить список игр===
					//*** Client -> Server ***
					//[4.C]	[+][server]
					//{"type": "Action", "action": "Get_gamelist"}
					//*** Server -> Client ***
					//[4.S]	[+][server]
					//{"type": "Action", "action": "Send_gamelist", "names": ["gamename","gamename2"]}
					
					if((string)jo["action"] == "Get_gamelist")
					{						
						PackageListNames PGobj = new PackageListNames(_koabras.Count);
						int i = 0;
						foreach(KoAbra game in _koabras)
						{
							PGobj.names[i] = game.name;
							i++;
						}
						
						string json = JsonConvert.SerializeObject(PGobj);
						
						SendThis(user, json);
					}
					
					//***************************************>
										
					//<***************************************
					//=== Получение списка карт ===
					//*** Client -> Server ***
					//[6.C]	[+][server]
					//{"type":"Action","action":"Get_maplist"}
					//*** Server -> Client ***
					//[10.S]	[+][server]
					//{"type": "Action", "action": "Send_maplist", "maps": ["name1", "name2", "name3"]}
					if((string)jo["action"] == "Get_maplist")
					{
						string path = @"Maps\maps.list";
						
						using (StreamReader sr = File.OpenText(path)) 
				        {
				            string s = "";
							string buf = "";
				            while ((buf = sr.ReadLine()) != null) 
				            {
				                s += buf;
				            }
							SendThis(user, s);
							LogOut(s);
				        }						
					}
					//***************************************>
					
					//<***************************************
					//===Пользователь желает присоединиться к игре===
					//*** Client -> Server ***
					//[5.C]	[+][server]
					//{"type": "Action", "action": "Join_game", "name": "gamename"}
					//*** Server -> Client ***
					//[8.S]	[+][server]
					//{"type": "Action", "action": "Join_game", "status": "OK", "reason": "0"}
					//[9.S]	[+][~server]
					//{"type": "Action", "action": "Join_game", "status": "ERR", "reason": "номер_причины"}
					//reason:
					//1 - данной игры не существует (создатель успел выйти прежде, чем пользователь соединился)
					//2 - ???
					if((string)jo["action"] == "Join_game")
					{
						int i = 0;
						bool found = false;
						foreach(KoAbra game in _koabras)
						{
							if(game.name == (string)jo["name"])
							{
								_koabras[i]._game_users.Add(user);
								found = true;
								break;
							}
							i++;
						}
						string mess;
						if(found)
						{
							mess = "{\"type\": \"Action\", \"action\": \"Join_game\", \"status\": \"OK\", \"reason\": \"0\"}";
						}
						else
						{
							mess = "{\"type\": \"Action\", \"action\": \"Join_game\", \"status\": \"ERR\", \"reason\": \"1\"}";							
						}
						LogOut(mess);
						SendThis(user, mess);	
					}
					//***************************************>
					
					//<***************************************
					//===Получить список игроков в текущей игре===
					//*** Client -> Server ***
					//[7.C]	[+][server]
					//{"type": "Action", "action": "Get_playerlist", "gamename": "name", "team": "NONE"}
					//{"type": "Action", "action": "Get_playerlist", "gamename": "name", "team": "A"}
					//{"type": "Action", "action": "Get_playerlist", "gamename": "name", "team": "B"}
					//*** Server -> Client ***
					//[7.S]	[+][server]
					//{"type": "Action", "action": "Send_playerlist", "names": ["player1", "player2", "player3"]}
					if((string)jo["action"] == "Get_playerlist")
					{
						if((string)jo["team"] == "NONE")
						{
							int i = 0;
							foreach(KoAbra game in _koabras)
							{
								if(game.name == (string)jo["gamename"])
								{
									PackageListNames PLNobj = new PackageListNames(_koabras[i]._game_users.Count);
									for(int j = 0; j < _koabras[i]._game_users.Count; j++)
									{
										PLNobj.action = "Send_playerlist";
										PLNobj.names[j] = _koabras[i]._game_users[j].Nick;
									}
									string json = JsonConvert.SerializeObject(PLNobj);						
									SendThis(user, json);
									break;
								}
								i++;
							}
						}
						if((string)jo["team"] == "A")
						{
							for(int i = 0; i < _koabras.Count; i++)
							{
								if(_koabras[i].name == (string)jo["gamename"])
								{
									PackageListNames PLNobj = new PackageListNames(_koabras[i]._a_game_users.Count);
									for(int j = 0; j < _koabras[i]._a_game_users.Count; j++)
									{
										PLNobj.action = "Send_playerlist";
										PLNobj.names[j] = _koabras[i]._a_game_users[j].Nick;
									}
									string json = JsonConvert.SerializeObject(PLNobj);						
									SendThis(user, json);
									break;
								}
							}
						}
						if((string)jo["team"] == "B")
						{
							for(int i = 0; i < _koabras.Count; i++)
							{
								if(_koabras[i].name == (string)jo["gamename"])
								{
									PackageListNames PLNobj = new PackageListNames(_koabras[i]._b_game_users.Count);
									for(int j = 0; j < _koabras[i]._b_game_users.Count; j++)
									{
										PLNobj.action = "Send_playerlist";
										PLNobj.names[j] = _koabras[i]._b_game_users[j].Nick;
									}
									string json = JsonConvert.SerializeObject(PLNobj);					
									SendThis(user, json);
									break;
								}
							}
						}
					}
					//***************************************>
					
					//<***************************************
					//===Создатель выбирает карту===
					//*** Client -> Server ***
					//[8.C]	[+][server]
					//{"type": "Action", "action": "Choose_map", "gamename": "gamename", "mapname": "map_name"}
					//[11.S][+][server]
					//{"type": "Action", "action": "Choose_map", "status": "OK", "reason": "0"}
					//[12.S][+][~server]
					//{"type": "Action", "action": "Choose_map", "status": "OK", "reason": "number"}
					if((string)jo["action"] == "Choose_map")
					{
						for(int i = 0; i < _koabras.Count; i++)
						{
							if(_koabras[i].name == (string)jo["gamename"])
							{
								string mess;
								if(_koabras[i].ChooseMap((string)jo["mapname"]))
								{
									mess = "{\"type\": \"Action\", \"action\": \"Choose_map\", \"status\": \"OK\", \"reason\": \"0\"}";
								}
								else
								{
									mess = "{\"type\": \"Action\", \"action\": \"Choose_map\", \"status\": \"ERR\", \"reason\": \"1\"}";
								}
								LogOut(mess);
								SendThis(user, mess);
								break;
							}
						}
					}
					//***************************************>
					
					//<***************************************
					//===Выбор команды A, B, NONE===
					//*** Client -> Server ***
					//[9.C]	[+][~server]
					//{"type": "Action", "action": "Choose_team", "gamename": "gamename", "team": "A"}
					//[13.S][+][server]
					//{"type": "Action", "action": "Choose_team", "status": "OK", "reason": "0"}
					if((string)jo["action"] == "Choose_team")
					{
						string mess;
						if((string)jo["team"] == "A")
						{
							for(int i = 0; i < _koabras.Count; i++)
							{
								if(_koabras[i].name == (string)jo["gamename"])
								{
									_koabras[i]._a_game_users.Add(user);
									int max_ind = _koabras[i]._a_game_users.Count-1;
									_koabras[i]._a_game_users[max_ind].team = "A";
									//for(int j = 0; j < _koabras[i]._game_users.Count; j++)
									//{
										int j = _koabras[i]._game_users.IndexOf(user);
										_koabras[i]._game_users.RemoveAt(j);
									//}
									mess = "{\"type\": \"Action\", \"action\": \"Choose_team\", \"status\": \"OK\", \"reason\": \"0\"}";
									LogOut(mess);
									SendThis(user, mess);
									break;
								}
							}
						}
						if((string)jo["team"] == "B")
						{
							for(int i = 0; i < _koabras.Count; i++)
							{
								if(_koabras[i].name == (string)jo["gamename"])
								{
									_koabras[i]._b_game_users.Add(user);
									int max_ind = _koabras[i]._b_game_users.Count-1;
									_koabras[i]._b_game_users[max_ind].team = "B";
									//for(int j = 0; j < _koabras[i]._game_users.Count; j++)
									//{
										int j = _koabras[i]._game_users.IndexOf(user);
										_koabras[i]._game_users.RemoveAt(j);
									//}
									mess = "{\"type\": \"Action\", \"action\": \"Choose_team\", \"status\": \"OK\", \"reason\": \"0\"}";
									LogOut(mess);
									SendThis(user, mess);
									break;
								}
							}
						}
						if((string)jo["team"] == "NONE")
						{
							for(int i = 0; i < _koabras.Count; i++)
							{
								if(_koabras[i].name == (string)jo["gamename"])
								{
									_koabras[i]._game_users.Add(user);
									int max_ind = _koabras[i]._game_users.Count-1;
									_koabras[i]._game_users[max_ind].team = "NONE";
									
									int j = _koabras[i]._a_game_users.IndexOf(user);
									if(j != -1)
									{
										_koabras[i]._a_game_users.RemoveAt(j);
									}
									if(j == -1)
									{
										j = _koabras[i]._b_game_users.IndexOf(user);
										if(j != -1)
										{
											_koabras[i]._b_game_users.RemoveAt(j);
										}
									}
									
									mess = "{\"type\": \"Action\", \"action\": \"Choose_team\", \"status\": \"OK\", \"reason\": \"0\"}";
									LogOut(mess);
									SendThis(user, mess);
									break;
								}
							}
						}
					}
					//***************************************>
					
					//<***************************************
					//===Начать игру===
					//*** Client -> Server ***
					//[10.C][+][server]
					//{"type": "Action", "action": "Start_game", "gamename": "gamename"}
					//*** Server -> Client ***
					//[15.S][+][server]
					//{"type": "Action", "action": "Start_game", "status": "OK", "reason": "0"}
					//[16.S][+][~server]
					//{"type": "Action", "action": "Start_game", "status": "OK", "reason": "num"}
					if((string)jo["action"] == "Start_game")
					{
						string mess;
						user.user_is_ready = true;
						
						int start_result = game.Start();
						string s_start_result = Convert.ToString(start_result);
						if(start_result == C_errors.ALL_RIGHT)
						{
							mess = "{\"type\": \"Action\", \"action\": \"Start_game\", \"status\": \"OK\", \"reason\": \"0\"}";	
							LogOut(mess);
							SendAll(mess);
							
							//<***************************************
							// отправляем пакет с информацией о местоположении городов
							//*** Server -> Client ***
							//[18.S][+][~Server]
							//{"type": "Action", "action": "Arrange_towns", "names": ["name1", "name2", "name3"], "xcoords": ["x1", "x2", "x3"], "ycoords": ["y1", "y2", "y3"]}
							
							PackageArrangeUnits PAUobj = new PackageArrangeUnits(game._a_game_users.Count);
							for(int i = 0; i < game._a_game_users.Count; i++)
							{
								PAUobj.names[i] = game._a_game_users[i].town.name;
								PAUobj.xcoords[i] = game._a_game_users[i].town.xcoords;
								PAUobj.ycoords[i] = game._a_game_users[i].town.ycoords;
							}
							string json = JsonConvert.SerializeObject(PAUobj);			
							SendTeam(json, "A");
							PackageArrangeUnits PAUobj2 = new PackageArrangeUnits(game._b_game_users.Count);
							for(int i = 0; i < game._b_game_users.Count; i++)
							{
								PAUobj2.names[i] = game._b_game_users[i].town.name;
								PAUobj2.xcoords[i] = game._b_game_users[i].town.xcoords;
								PAUobj2.ycoords[i] = game._b_game_users[i].town.ycoords;
							}
							
							json = JsonConvert.SerializeObject(PAUobj2);							
							SendTeam(json, "B");
							//***************************************>
							
							//<***************************************
							// отправляем пакет с информацией о местоположении героев
							//*** Server -> Client ***
							//[18.S][+][~Server]
							//{"type": "Action", "action": "Arrange_heroes", "names": ["name1", "name2", "name3"], "xcoords": ["x1", "x2", "x3"], "ycoords": ["y1", "y2", "y3"]}
							
							for(int i = 0; i < game._a_game_users.Count; i++)
							{
								PAUobj.action = "Arrange_heroes";
								PAUobj.names[i] = game._a_game_users[i].Nick;
								PAUobj.xcoords[i] = game._a_game_users[i].xcoords;
								PAUobj.ycoords[i] = game._a_game_users[i].ycoords;
							}
							json = JsonConvert.SerializeObject(PAUobj);					
							SendTeam(json, "A");
							for(int i = 0; i < game._b_game_users.Count; i++)
							{
								PAUobj2.action = "Arrange_heroes";
								PAUobj2.names[i] = game._b_game_users[i].Nick;
								PAUobj2.xcoords[i] = game._b_game_users[i].xcoords;
								PAUobj2.ycoords[i] = game._b_game_users[i].ycoords;
							}
							json = JsonConvert.SerializeObject(PAUobj2);
							SendTeam(json, "B");
							//***************************************>
						}
						else
						{
							mess = "{\"type\": \"Action\", \"action\": \"Start_game\", \"status\": \"ERR\", \"reason\": \"" + s_start_result + "\"}";
							LogOut(mess);
							SendThis(user, mess);
						}
					}
					//***************************************>
					
					//<***************************************
					//=== Выйти героем из города ===
					//*** Client -> Server ***
					//[11.C]	[+][server]
					//{"type": "Action", "action": "Get_the_hero_out_of_town"}
					//*** Server -> Client ***
					//[19.S]	[+][server]
					//To this user
					//{"type": "Action", "action": "Get_the_hero_out_of_town", "status": "OK", "reason": "0"}
					//To other users of this team
					//{"type": "Action", "action": "Set_the_hero_out_of_town", "name": "nickname"}
					if((string)jo["action"] == "Get_the_hero_out_of_town")
					{
						user.hero_in_town = false;
						string mess = "{\"type\": \"Action\", \"action\": \"Get_the_hero_out_of_town\", \"status\": \"OK\", \"reason\": \"0\"}";
						LogOut(mess);
						SendThis(user, mess);
						mess = "{\"type\": \"Action\", \"action\": \"Get_the_hero_out_of_town\", \"name\": \"" + user.Nick + "\"}";
						SendTeamWithExceptionThis(mess, user.team, user.Nick);
					}
					//***************************************>
					
					//<***************************************
					//=== Войти героем в город ===
					//*** Client -> Server ***
					//[12.C]	[+][server]
					//{"type": "Action", "action": "Enter_the_hero_in_town"}
					//*** Server -> Client ***
					//[20.S]	[+][server]
					//To this user
					//{"type": "Action", "action": "Enter_the_hero_in_town", "status": "OK", "reason": "0"}
					//To other users of this team
					//{"type": "Action", "action": "The_hero_was_entered_in_town", "name": "nickname"}
					if((string)jo["action"] == "Enter_the_hero_in_town")
					{
						// если герой неподалеку от города, он может войти в него
						if(user.xcoords - user.town.xcoords <= 2 && user.xcoords - user.town.xcoords >= -2
							&& user.ycoords - user.town.ycoords <= 2 && user.ycoords - user.town.ycoords >= -2)
						{
							user.hero_in_town = true;
							string mess = "{\"type\": \"Action\", \"action\": \"Enter_the_hero_in_town\", \"status\": \"OK\", \"reason\": \"0\"}";
							LogOut(mess);
							SendThis(user, mess);
							mess = "{\"type\": \"Action\", \"action\": \"The_hero_was_entered_in_town\", \"name\": \"" + user.Nick + "\"}";
							SendTeamWithExceptionThis(mess, user.team, user.Nick);
						}
						else
						{
							// герой слишком далеко от города
							user.hero_in_town = false;
							string mess = "{\"type\": \"Action\", \"action\": \"Enter_the_hero_in_town\", \"status\": \"ERR\", \"reason\": \"1\"}";
							LogOut(mess);
							SendThis(user, mess);
						}
					}
					//***************************************>
					
					//<***************************************
					//=== Герой ходит ===
					//*** Client -> Server ***
					//[13.C]	[+][server]
					//{"type": "Action", "action": "Hero_goes", "xcoords": "x", "ycoords": "y"}
					//*** Server -> Client ***
					//[21.S]	[+][server]
					//{"type": "Action", "action": "Hero_goes", "name": "nickname", "xcoords": "x", "ycoords": "y"}
					if((string)jo["action"] == "Hero_goes")
					{
						string s_x = (string)jo["xcoords"];
						user.xcoords = Convert.ToInt32(s_x);
						string s_y = (string)jo["ycoords"];
						user.ycoords = Convert.ToInt32(s_y);
						
						string mess = "{\"type\": \"Action\", \"action\": \"Hero_goes\", \"name\": \"" + user.Nick + "\", \"xcoords\": \"" + s_x + "\", \"ycoords\": \"" + s_y + "\"}";
						LogOut(mess);
						// отправить сообщение команде, за исключением текущего клиента
						SendTeamWithExceptionThis(mess, user.team, user.Nick);
					}
					//***************************************>
					
					//<***************************************
					//=== Установить флаг на ресурсе ===
					//*** Client -> Server ***
					//[14.C]	[+][server]
					//{"type": "Action", "action": "Set_the_flag_on_the_resource", "xcoords": "x", "ycoords": "y"}
					//*** Server -> Client ***
					//[22.S]	[+][server]
					//To this user
					//{"type": "Action", "action": "Set_the_flag_on_the_resource", "status": "OK", "reason": "0"}
					//{"type": "Action", "action": "Set_the_flag_on_the_resource", "status": "ERR", "reason": "1"}
					//To other users of this team
					//{"type": "Action", "action": "Someone_took_the_resource", "name": "nickname", "xcoords": "x", "ycoords": "y"}
					if((string)jo["action"] == "Set_the_flag_on_the_resource")
					{
						string s_x = (string)jo["xcoords"];
						int x = Convert.ToInt32(s_x);
						string s_y = (string)jo["ycoords"];
						int y = Convert.ToInt32(s_y);
						if(game.map.flag_res[x, y] == "NONE")
						{
							game.map.flag_res[x, y] = user.Nick;
							string mess = "{\"type\": \"Action\", \"action\": \"Set_the_flag_on_the_resource\", \"status\": \"OK\", \"reason\": \"0\"}";
							LogOut(mess);
							SendThis(user, mess);
							mess = "{\"type\": \"Action\", \"action\": \"Someone_took_the_resource\", \"name\": \"" + user.Nick + "\", \"xcoords\": \"" + s_x + "\", \"ycoords\": \"" + s_y + "\"}";
							SendTeamWithExceptionThis(mess, user.team, user.Nick);
							
							// добавляем ресурс в список ресурсов города
							Resource res = new Resource(game.map.resources[x, y], x, y);
							user.town._users_resources.Add(res);
						}
						else
						{
							// ресурс уже занят другим героем
							string mess = "{\"type\": \"Action\", \"action\": \"Set_the_flag_on_the_resource\", \"status\": \"ERR\", \"reason\": \"1\"}";
							LogOut(mess);
							SendThis(user, mess);
						}
					}
					//***************************************>
				}
				//***************************************>
				
				//<***************************************
				//*** проверка чтения карты из файла ***
				if((string)jo["message"] == "!Read")
				{
					string path = @"Maps\default.map";
					
					// Open the file to read from.
	        		using (StreamReader sr = File.OpenText(path)) 
			        {
			            string s = "";
						string buf = "";
			            while ((buf = sr.ReadLine()) != null) 
			            {
			                s += buf;
			            }
						Console.WriteLine(s);
						JObject jobj = JObject.Parse(s);
						JArray jarr = (JArray)jobj["resources"];
						Console.WriteLine((string)jarr[0][1]+" "+(string)jobj["name"]);
			        }
				}
				//***************************************>
				
            }
			// иначе, аутентифицироваться
            else
            {	
				//<***************************************
				//*** Client -> Server ***
				//[2.C]	[+][server][client]
				//{"type": "ChatMessage", "message": "nickname"}
				JObject jobj = JObject.Parse(message);
				PackageChatAuth PCAobj = new PackageChatAuth();
				PCAobj.login = (string)jobj["message"];
				bool all_right = true;
				for(int i = 0; i < _users.Count; i++)
				{
					if(_users[i].Nick == (string)jobj["message"])
					{
						all_right = false;
					}
				}
				if(all_right)
				{
					user.Nick = (string)jobj["message"];
					PCAobj.status = "OK";
					PCAobj.reason = "0";
					
					user.Authentificated = true;
                	LogIn(String.Format("User Connected: {0}", user.Nick));
					
					string mess = "{\"type\": \"OtherChatAuth\", \"login\": \"" + user.Nick + "\"}";
					LogOut(mess);
					SendAllWithExceptionThis(mess, user.Nick);
				}
				else
				{
					PCAobj.status = "ERR";
					PCAobj.reason = "1";	// пользователь с таким именем подключен к серверу
				}
				//***************************************>
				             
				LogIn(message);
                
				//<***************************************
				//*** Server -> Client ***
				//[2.S]	[+][server][client]
				// {type: ChatAuth, login: nickname}
				string PCAjson = JsonConvert.SerializeObject(PCAobj);
				SendThis(user, PCAjson);
				//***************************************>
            }
        }

        private void LogIn(string message)
        {
            if (Log)
                Console.WriteLine(string.Format("{0} > {1}", DateTime.Now.ToLongTimeString(), message));
        }

        private void LogOut(string message)
        {
            if (Log)
                Console.WriteLine(string.Format("{0} < {1}", DateTime.Now.ToLongTimeString(), message));
        }
    }
}
